﻿using SoundEngine3.AudioProcessing.Algorithms;
using SoundEngine3.DspPlugins.Meta;
using SoundEngine3.MIDI.Meta;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Modules.HighLevel
{
    /// <summary>
    /// http://en.wikiaudio.org/ADSR_envelope
    /// </summary>
    [ModuleDisplay("Env DR")]
    public class EnvelopeDRModule : BaseModule
    {
        private EnvelopeGenerator mADSREnvelop = new EnvelopeGenerator();

        private double mLastValue;

        public readonly ModuleInput Gate;
        public readonly ModuleOutput Output;
        public readonly ModuleOutput OnRunAndIdle;
        
        public readonly ParameterPropertiesDirect Decay;
        public readonly ParameterPropertiesDirect Release;

        public EnvelopeDRModule(ModuleManager manager)
            : base(manager)
        {
            mADSREnvelop.AttackRate = 0;

            AddInput(Gate = new ModuleInput("On", IOType.Midi, this));
            AddOutput(Output = new ModuleOutput("Out", "Output", IOType.Audio, this));
            AddOutput(OnRunAndIdle = new ModuleOutput("Rai", "OnRunAndIdle", IOType.Midi, this));
            
            InputModuleParameter release = new InputModuleParameter(this, "Rel", mADSREnvelop.ReleaseRate, IOType.Audio, InputModuleType.OnlyDirect);
            release.Label = "Release";
            release.MinValue = 0;
            release.MaxValue = 44100;
            release.DirectGet += delegate { return mADSREnvelop.ReleaseRate; };
            release.DirectSet += delegate (double value) { mADSREnvelop.ReleaseRate = value; };
            AddModuleParameter(release);
            
            InputModuleParameter decayRate = new InputModuleParameter(this, "Dec", mADSREnvelop.DecayRate, IOType.Audio, InputModuleType.OnlyDirect);
            decayRate.Label = "Decay";
            decayRate.MinValue = 0;
            decayRate.MaxValue = 44100;
            decayRate.DirectGet += delegate { return mADSREnvelop.DecayRate; };
            decayRate.DirectSet += delegate (double value) { mADSREnvelop.DecayRate = value; };
            AddModuleParameter(decayRate);
        }
        
        public override void Process()
        {
            if (Gate.IsConnected && Output.IsConnected)
            {
                bool gate = false;

                int length = Output.AudioBuffer.Length;
                for (int i = 0; i < length; i++)
                {
                    var state = Gate.GetMidiState(i);

                    if (state != 0)
                    {
                        if (state == SoundEngine3.MIDI.Meta.MidiStatus.NOTE_ON)
                        {
                            gate = true;
                        }
                    }

                    if (gate)
                    {
                        //mADSREnvelop.Reset();
                        mADSREnvelop.Gate(true);
                    }

                    double value = mADSREnvelop.Process();

                    if (gate)
                    {
                        mADSREnvelop.Gate(false);
                        gate = false;
                    }

                    Output.AudioBuffer[i] = value;

                    if (mLastValue != value)
                    {
                        if (value == 0.0)
                        {
                            var noteOff = MidiMessage.NoteOff;
                            noteOff.DeltaFrame = i;
                            OnRunAndIdle.AddMidiMessage(noteOff);
                        }
                        else if(mLastValue == 0.0)
                        {
                            var noteOn = MidiMessage.NoteOn;
                            noteOn.DeltaFrame = i;
                            OnRunAndIdle.AddMidiMessage(noteOn);
                        }
                    }

                    mLastValue = value;
                }
            }
        }
    }
}
